package com.zst.rabbitmq.monitor;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import com.alibaba.fastjson.JSONArray;
import com.zst.rabbitmq.monitor.dto.ClusterNode;
import com.zst.rabbitmq.monitor.dto.Node;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

public class Monitor {
    private static final Logger logger = LoggerFactory.getLogger(Monitor.class);

    /** 访问rabbitmq的用户名*/
    private String username;
    /** 访问rabbitmq的密码*/
    private String password;
    /** rabbitmq的节点地址*/
    private String url;
    /** rabbitmq的端口*/
    private int port;
    /** rabbitmq的http地址是否使用https*/
    private boolean https;
    /** spring的RestTemplate工具类*/
    private RestTemplate restTemplate;

    private static final String API_NODES = "/api/nodes";
    private static final String API_OVERVIEWS = "/api/overview";

    private Monitor(String username, String password, String url, int port, boolean https) {
        this.username = username;
        this.password = password;
        this.url = url;
        this.port = port;
        this.https = https;

        // 创建resttemplate,设置固定的3s超时时间和账号密码认证
        this.restTemplate = new RestTemplateBuilder().requestFactory(() -> {
            SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
            requestFactory.setConnectTimeout(3 * 1000);
            return requestFactory;
        }).basicAuthentication(username, password).build();
    }

    /**
     * 获取节点所在的集群中所有节点的状态
     * @return
     * @throws Exception
     */
    public List<ClusterNode> clusterNodes() throws Exception{
        return listReturnCallTemplate(createApiUrl(API_NODES), ClusterNode::map);
    }

    /**
     * 获取当前节点信息
     * @return
     */
    public Node nodeOverview() throws Exception {
        return singleReturnCallTemplate(createApiUrl(API_OVERVIEWS), Node::map);
    }

    /**
     * 调用返回列表数据的api的通用模板
     * @param url http api地址
     * @param translator json字符串到目标类的转换函数
     * @return 接口返回的数据经过转换函数转换的结果列表
     * @throws Exception 当api调用失败时会抛出异常
     */
    private <T> List<T> listReturnCallTemplate(String url, Function<String, T> translator) throws Exception {
        String resp = httpCall(url);
        if (StringUtils.isEmpty(resp)) {
            return Collections.emptyList();
        }

        JSONArray jsonArray = JSONArray.parseArray(resp);
        List<T> list = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            list.add(translator.apply(jsonArray.getString(i)));
        }

        return list;
    }

    /**
     * 调用返回单个json数据的api的通用模板
     * @param url http api地址
     * @param tranlastor json字符串到目标类的转换函数
     * @return 经过转换的结果
     * @throws Exception 当api调用失败时会抛出异常
     */
    private <T> T singleReturnCallTemplate(String url, Function<String, T> tranlastor) throws Exception {
        String resp = httpCall(url);
        if (StringUtils.isEmpty(resp)) {
            return null;
        }

        return tranlastor.apply(resp);
    }

    /**
     * 生成api url
     * @param route api路径
     * @return
     */
    private String createApiUrl(String route) {
        return this.https ? "https://" + url + ":" + port + route : "http://" + url + ":" + port + route;
    }

    /**
     * 调用url
     * @param url
     * @return
     * @throws Exception 当访问
     */
    private String httpCall(String url) throws Exception {
        try {
            return this.restTemplate.getForObject(url, String.class);
        } catch (Exception e) {
            throw new Exception("RabbitMQ HTTP API call failed, url=>" + url, e);
        }
    }

    /**
     * Monitor Builder类
     */
    public static class MonitorBuilder {
        private String username;
        private String password;
        private String url;
        private int port;
        private Boolean https;

        public MonitorBuilder username(String username) {
            this.username = username;
            return this;
        }

        public MonitorBuilder password(String password) {
            this.password = password;
            return this;
        }


        public MonitorBuilder url(String url) {
            this.url = url;
            return this;
        }

        public MonitorBuilder port(int port) {
            this.port = port;
            return this;
        }

        public MonitorBuilder https(boolean https) {
            this.https = https;
            return this;
        }

        public Monitor build() {
            // 如果没有设置端口，则取默认的15672端口
            if (this.port == 0) {
                this.port = 15672;
            }

            if (this.https == null) {
                this.https = false;
            }

            if (StringUtils.isEmpty(this.username)) {
                throw new IllegalArgumentException("username cannot empty");
            }

            if (StringUtils.isEmpty(this.password)) {
                throw new IllegalArgumentException("password cannot empty");
            }

            if (StringUtils.isEmpty(url)) {
                throw new IllegalArgumentException("url cannot empty");
            }
            return new Monitor(this.username, this.password, this.url, this.port, this.https);
        }
    }
}
